package ch.il07.converter.business.impl;

import java.util.List;

import ch.il07.converter.business.Converter;
import ch.il07.converter.persistence.Persistence;
import ch.il07.converter.persistence.PersistenceHelper;
import ch.il07.converter.persistence.entities.Measurand;
import ch.il07.converter.persistence.entities.Unit;
import ch.il07.converter.persistence.exceptions.NoSuchMeasurandException;
import ch.il07.converter.persistence.exceptions.NoSuchUnitException;
import ch.il07.converter.persistence.exceptions.NoSuchUserException;
import ch.il07.converter.persistence.exceptions.PersistenceException;
import ch.il07.utils.logging.Level;
import ch.il07.utils.logging.Logger;
import ch.il07.utils.logging.LoggerHelper;

public class ConverterImpl implements Converter {

	private static final Logger LOGGER = LoggerHelper.getLogger();

	/**
	 * {@inheritDoc}
	 */
	@Override
	public Double calculate(String owner, String measurandName,
			String fromUnitName, String toUnitName, Double amount)
			throws NoSuchMeasurandException, NoSuchUserException,
			NoSuchUnitException {

		if (!ServiceHelper.parameterListNotNull(measurandName, fromUnitName,
				toUnitName, amount)) {
			throw new IllegalArgumentException(
					"You HAVE TO give in measurandName, fromUnitName, toUnitName AND amount!");
		}

		List<Unit> units;
		try {
			Persistence persistence = PersistenceHelper.getPersistence();
			Measurand m = persistence.getMeasurand(measurandName, owner);
			if (m == null) {
				if (LOGGER.isLoggable(Level.INFO)) {
					LOGGER.info("measurand [" + measurandName
							+ "] not found for user [" + owner + "]");
				}
				m = persistence.getMeasurand(measurandName, null);
				if (m == null) {
					NoSuchMeasurandException e = new NoSuchMeasurandException(
							"measurand [" + measurandName + "] does not exists");
					if (LOGGER.isLoggable(Level.ERROR)) {
						LOGGER.error(e);
					}
					throw e;
				}
			}
			units = m.getUnits();
			units.add(m.getDefaultUnit());
		} catch (PersistenceException persEx) {
			LOGGER.error(persEx);
			return null;
		}

		Unit fromUnit = null;
		Unit toUnit = null;
		for (Unit unit : units) {
			if (fromUnitName.equals(unit.getName())) {
				fromUnit = unit;
			}

			if (toUnitName.equals(unit.getName())) {
				toUnit = unit;
			}
		}
		if (fromUnit == null) {
			throw new NoSuchUnitException("Unit [" + fromUnitName
					+ "] doesn't exist");
		} else if (toUnit == null) {
			throw new NoSuchUnitException("Unit [" + toUnitName
					+ "] doesn't exist");
		}
		return convert(fromUnit, toUnit, amount);
	}

	/**
	 * Converts the units, credit belongs to philipp.gressly (author).
	 * 
	 * @param fromUnit
	 *            Source unit
	 * @param toUnit
	 *            Destination unit
	 * @param amount
	 *            Number
	 * @return result
	 */
	public Double convert(Unit fromUnit, Unit toUnit, Double amount) {
		Double fd = fromUnit.getDivisor();
		Double td = toUnit.getDivisor();
		Double fs = fromUnit.getShift();
		Double ts = toUnit.getShift();

		Double baseValue = fd * amount + fs;
		Double newValue = (baseValue - ts) / td;

		return newValue;
	}
}